package sa4

import javax.servlet.http._
import scala.xml._
import scala.collection.JavaConversions._
import scala.util.matching.Regex
import javax.servlet.ServletConfig
import scala.io.Source._
import java.io._
import org.squeryl.PrimitiveTypeMode._
import org.squeryl.KeyedEntity
import org.squeryl.annotations.OptionType
import org.squeryl.SessionFactory
import org.squeryl.Session
import org.squeryl.adapters.H2Adapter
import java.sql.Timestamp
import java.sql.Date
import scala.collection.mutable.HashMap

class Student(var nOfcourses: Int, val name: String, val surname: String, val password: String, var course1: Long, var course2: Long, var course3: Long, var course4: Long, var course5: Long) extends KeyedEntity[Long] {
  val id = 0L
  def this() = this(0, "", "", "", 0, 0, 0, 0, 0)
}
// The database schema for our books
object Students extends org.squeryl.Schema {
  val students = table[Student]
}

class Course(val name: String, var descritpion: String, val prof: String) extends KeyedEntity[Long] {
  val id = 0L
  def this() = this("", "", "")
}

// The database schema for our books
object Courses extends org.squeryl.Schema {
  val courses = table[Course]
}

class Time(val day: String, val start: String, val end: String, val cID: Long) extends KeyedEntity[Long] {
  val id = 0L
  def this() = this("", "", "", 0)
}

object Times extends org.squeryl.Schema {
  val times = table[Time]
}

class App extends HttpServlet {

  var studentName = ""
  var studentSurname = ""
  var string: String = ""

  override def init(config: ServletConfig) {
    super.init(config)

    Class.forName("org.h2.Driver")
    SessionFactory.concreteFactory = Some(() =>
      Session.create(
        java.sql.DriverManager.getConnection("jdbc:h2:" + System.getProperty("user.dir") + "/test"),
        new H2Adapter))

    transaction {
      Students.drop
      Students.create
      Courses.drop
      Courses.create
      Times.drop
      Times.create
    }
    transaction {
      val s4 = Courses.courses.insert(new Course("SA4", "Software Atelier 4", "Nystrom"))
      val ai = Courses.courses.insert(new Course("IKM-AI", "Artifficial Intelligence", "Gambardella"))
      val nc = Courses.courses.insert(new Course("NC", "Net-Centric Computing", "Binder"))
      val algo = Courses.courses.insert(new Course("ALGO", "Algorithms and Data Structures", "Carzaniga"))
      val pf2 = Courses.courses.insert(new Course("PF2", "Programming Fundamentals II", "Hauswirth"))
      val cs = Courses.courses.insert(new Course("CS", "Computational Science", "Krause"))

      val nico = Students.students.insert(new Student(1, "nico", "z", "lol", s4.id, 0, 0, 0, 0))
      val virgie = Students.students.insert(new Student(0, "virgie", "b", "t", 0, 0, 0, 0, 0))

      val s4time1 = Times.times.insert(new Time("Monday", "13:30", "15:15", s4.id))
      val s4time2 = Times.times.insert(new Time("Wednesday", "13:30", "15:15", s4.id))
      val s4time3 = Times.times.insert(new Time("Friday", "13:30", "15:15", s4.id))
      val aitime1 = Times.times.insert(new Time("Monday", "10:30", "12:15", ai.id))
      val aitime2 = Times.times.insert(new Time("Friday", "10:30", "12:15", ai.id))
      val nctime1 = Times.times.insert(new Time("Thursday", "08:30", "10:15", nc.id))
      val nctime2 = Times.times.insert(new Time("Friday", "08:30", "10:15", nc.id))
      val algotime1 = Times.times.insert(new Time("Thursday", "10:30", "12:15", algo.id))
      val algotime2 = Times.times.insert(new Time("Tuesday", "08:30", "10:15", algo.id))
      val pf2time1 = Times.times.insert(new Time("Wednesday", "08:30", "10:15", pf2.id))
      val pf2time2 = Times.times.insert(new Time("Thursday", "13:30", "15:15", pf2.id))
      val cstime1 = Times.times.insert(new Time("Tuesday", "10:30", "12:15", cs.id))
      val cstime2 = Times.times.insert(new Time("Monday", "08:30", "10:15", cs.id))

    }
    framework.addRoute("/", ciao)
    framework.addRoute("/login", logInPage)
    framework.addRoute("/mainPage", mainPage)
    framework.addRoute("/newMain.css", css)
    framework.addRoute("/jquery-1.7.2.js", jqueryFile)
    framework.addRoute("/signup", registerPage)
    framework.addRoute("/setResp", setResp)
    framework.addRoute("/listallcourses", sendAllCourses)
    framework.addRoute("/listmycourses", sendMyCourses)
    framework.addRoute("/schedule", sendSchedule)
    framework.addRoute("/enroll", addCourse)
    framework.addRoute("/unenroll", removeCourse)
    framework.addRoute("/logout", logOut)
  }
  val framework = new MainFramework

  override def service(req: HttpServletRequest, resp: HttpServletResponse) {

    framework.currentContext() = new Context(req, resp)
    if (req.getSession.isNew) {
      studentName = ""
      studentSurname = ""
      resp.sendRedirect("/")
    }
    if (framework.tableDispatch(req, resp))
      return
    resp.sendError(HttpServletResponse.SC_NOT_FOUND)
  }

  def ciao(req: HttpServletRequest, resp: HttpServletResponse) {
    val xml = XML.loadFile("htmlContents/welcomePage.html")
    val html = framework.prepareForm(xml)
    resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
  }

  def addCourse(req: HttpServletRequest, resp: HttpServletResponse) {
    val id = req.getParameter("String").toLong
    enroll(id)
  }

  def removeCourse(req: HttpServletRequest, resp: HttpServletResponse) {
    val id = req.getParameter("String").toLong
    unenroll(id)
  }

  def formEnter(funct: String): Form = {
    val formEnter = new Form("post", "" + funct + "(name, surname, password)")
    formEnter.addInput("Name", "textarea", "name")
    formEnter.addInput("Surname", "textarea", "surname")
    formEnter.addInput("Password", "textarea", "password")
    formEnter.addInput("", "submit", "Log In")
    return formEnter
  }

  def registerPage(req: HttpServletRequest, resp: HttpServletResponse) {
    val formRegister = formEnter("register")
    val ns = framework.fillTemplateLogin(formRegister.makeForm().toString(), "register").toString()
    val ns2 = XML.loadString(ns)
    val html = framework.prepareForm(ns2)
    resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
  }

  def logInPage(req: HttpServletRequest, resp: HttpServletResponse) {
    val formLogin = formEnter("logIn")
    val string = formLogin.makeForm().toString() + "<p>Not registered yet? <a href=\"/signup\">Register to MyCorsi</a></p>"
    val ns = framework.fillTemplateLogin(string, "login").toString()
    val ns2 = XML.loadString(ns)
    val html = framework.prepareForm(ns2)
    resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
    //lol
  }

  def logOut(req: HttpServletRequest, resp: HttpServletResponse) {
    val sessions = framework.sessionMap.keys
    for (session <- sessions) {
      if (session == framework.Count.getIdSession) {
        framework.sessionMap -= session
      }
    }
    studentName = ""
    studentSurname = ""
    string = ""
    resp.sendRedirect("/")

  }

  def logIn(name: String, surname: String, password: String): scala.xml.Elem = {
    transaction {
      var f: scala.xml.Elem = null
      for (s <- Students.students) {
        if (s.name == name && s.surname == surname && s.password == password) {
          f = XML.loadFile("htmlContents/loggedin.html")
        }
      }
      if (f == null) {
        f = XML.loadFile("htmlContents/errorLoggedin.html")
      }
      return f

    }
  }

  def mainPage(req: HttpServletRequest, resp: HttpServletResponse) {
    var f: scala.xml.Elem = null
    setStudent()
    val sessions = framework.sessionMap.keys
    for (session <- sessions) {
      if (session == framework.Count.getIdSession) {
        string += framework.sessionMap(session)
      }
    }
    if (string != "") {
      f = mainPageView(string)
      val html = framework.prepareForm(f)
      resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
    } else {
      resp.sendRedirect("/")
    }
    resp.flushBuffer
  }

  def css(req: HttpServletRequest, resp: HttpServletResponse) = {
    val css = scala.io.Source.fromFile("templates/newMain.css").getLines.mkString
    resp.setContentType("text/css")
    resp.getWriter.write(css)
  }

  def jqueryFile(req: HttpServletRequest, resp: HttpServletResponse) = {
    val js = scala.io.Source.fromFile("templates/jquery-1.7.2.js").toArray
    resp.getWriter.write(js)
    resp.setContentType("text/javascript")
  }

  def writeXML(resp: HttpServletResponse, ns: NodeSeq) = resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(ns))

  def send(resp: HttpServletResponse, funct: String) = {
    resp.setContentType("text/html")
    resp.setStatus(HttpServletResponse.SC_OK)
    if (funct == "listAllCourses") writeXML(resp, mainPageView(studentName + " " + studentSurname))
    if (funct == "listMyCourses") writeXML(resp, viewForMyCourses(studentName + " " + studentSurname))
    if (funct == "schedule") writeXML(resp, schedule())

  }

  def setResp(req: HttpServletRequest, resp: HttpServletResponse) {
    resp.setStatus(200)
    resp.setContentLength(0)
    resp.setContentType("application/xml")
  }

  def sendAllCourses(req: HttpServletRequest, resp: HttpServletResponse) {
    send(resp, "listAllCourses")
  }

  def sendMyCourses(req: HttpServletRequest, resp: HttpServletResponse) {
    send(resp, "listMyCourses")
  }

  def sendSchedule(req: HttpServletRequest, resp: HttpServletResponse) {
    send(resp, "schedule")
  }

  def mainPageView(string: String) = {
    val js =
      """
      function updateTable(xml) {
        $("#table").html(xml);
      }
      
      function updateAllFile(xml){
    	$("#bodyhtml").html(xml);	
      }
      
      function updateAllCourses() {
        $.get("/listallcourses", function (x) { updateAllFile(x); });
      }
      
      function updateMyCourses() {
        $.get("/listmycourses", function (x) { updateAllFile(x); });
      }
      
      function updateSchedule() {
        $.get("/schedule", function (x) { updateTable(x); });
      }
      
      function updateSchedule() {
        $.get("/schedule", function (x) { updateTable(x); });
      }
      
      function displayMyCourses(){
    	$.get("/setResp", function() { updateMyCourses(); });
      }
      
      function displayCourses(){
    	$.get("/setResp", function() { updateAllCourses(); });
      }
      
      function displaySchedule(){
    	$.get("/setResp", function() { updateSchedule(); });
      }
    
	  $(document).ready(function() {
        $("#allcourses").click(function () {
    		displayCourses();	
        });
      
      $("#mycourses").click(function () {
    		displayMyCourses();	
        });
      
      $("#schedule").click(function () {
    		displaySchedule();	
        });
      
      $(".enroll").click(function(e){
    		var x = e.target.id;
    		$.get("/enroll", {String: x});
      
		  }); 
      
      $(".unenroll").click(function(e){
    		var x = e.target.id;
    		$.get("/unenroll", {String: x});
      
		  });
  });
	    """

    <html>
      <head>
        <title>MyCorsi</title>
        <link rel="stylesheet" href="/newMain.css"></link>
        <script type="text/javascript" src="/jquery-1.7.2.js"></script>
        <script type="text/javascript">{ new Unparsed(js) /* use Unparsed to avoid escaping " to &quot; */ }</script>
      </head>
      <body id="bodyhtml">
        <section class="body">
          <aside>
            <p>Logged in as { string }</p>
            <p><a href="/logout">Logout</a></p>
          </aside>
          <nav>
            <ul>
              <li><button id="mycourses">My Courses</button></li>
              <li><button id="allcourses">All Courses</button></li>
              <li><button id="schedule">Schedule</button></li>
            </ul>
          </nav>
          <article id="table">
            { listAllCourses() }
          </article>
        </section>
      </body>
    </html>
  }

  def viewForMyCourses(string: String) = {
    val js =
      """
      function updateTable(xml) {
        $("#table").html(xml);
      }
      
      function updateAllFile(xml){
    	$("#bodyhtml").html(xml);	
      }
      
      function updateAllCourses() {
        $.get("/listallcourses", function (x) { updateAllFile(x); });
      }
      
      function updateMyCourses() {
        $.get("/listmycourses", function (x) { updateAllFile(x); });
      }
      
      function updateSchedule() {
        $.get("/schedule", function (x) { updateTable(x); });
      }
      
      function updateSchedule() {
        $.get("/schedule", function (x) { updateTable(x); });
      }
      
      function displayMyCourses(){
    	$.get("/setResp", function() { updateMyCourses(); }); // step and when done, poll to update the display
      }
      
      function displayCourses(){
    	$.get("/setResp", function() { updateAllCourses(); }); // step and when done, poll to update the display
      }
      
      function displaySchedule(){
    	$.get("/setResp", function() { updateSchedule(); }); // step and when done, poll to update the display
      }
    
	  $(document).ready(function() {
        $("#allcourses").click(function () {
    		displayCourses();	
        });
      
      $("#mycourses").click(function () {
    		displayMyCourses();	
        });
      
      $("#schedule").click(function () {
    		displaySchedule();	
        });
      
      $(".enroll").click(function(e){
    		var x = e.target.id;
    		$.get("/enroll", {String: x});
      
		  }); 
      
      $(".unenroll").click(function(e){
    		var x = e.target.id;
    		$.get("/unenroll", {String: x});
      
		  });
  });
	    """

    <html>
      <head>
        <title>MyCorsi</title>
        <link rel="stylesheet" href="/newMain.css"></link>
        <script type="text/javascript" src="/jquery-1.7.2.js"></script>
        <script type="text/javascript">{ new Unparsed(js) /* use Unparsed to avoid escaping " to &quot; */ }</script>
      </head>
      <body id="bodyhtml">
        <section class="body">
          <aside>
            <p>Logged in as { string }</p>
            <p><a href="/logout">Logout</a></p>
          </aside>
          <nav>
            <ul>
              <li><button id="mycourses">My Courses</button></li>
              <li><button id="allcourses">All Courses</button></li>
              <li><button id="schedule">Schedule</button></li>
            </ul>
          </nav>
          <article id="table">
            { listMyCourses() }
          </article>
        </section>
      </body>
    </html>
  }

  def schedule(): NodeSeq = {
    var stud: Student = null
    transaction {
      for (student <- Students.students) {
        if (student.name == studentName && student.surname == studentSurname) {
          stud = student
        }
      }
    }
    <table>
      <tr>
        <td>
        </td>
        <td>
          Monday
        </td>
        <td>
          Tuesday
        </td>
        <td>
          Wednesday
        </td>
        <td>
          Thursday
        </td>
        <td>
          Friday
        </td>
      </tr>
      <tr>
        <td>
          08:30 - 10:30
        </td>
        {
          transaction {
            checkDate("08:30", stud)
          }
        }
      </tr>
      <tr>
        <td>
          10:30 - 12:30
        </td>
        {
          transaction {
            checkDate("10:30", stud)
          }
        }
      </tr>
      <tr>
        <td>
          13:30 - 15:30
        </td>
        {
          transaction {
            checkDate("13:30", stud)
          }
        }
      </tr>
      <tr>
        <td>
          15:30 - 17:30
        </td>
        {
          transaction {
            checkDate("15:30", stud)
          }
        }
      </tr>
    </table>

  }

  def checkDate(startHour: String, student: Student): NodeSeq = {
    <td>
      { printCourse(startHour, "Monday", student) }
    </td>
    <td>
      { printCourse(startHour, "Tuesday", student) }
    </td>
    <td>
      { printCourse(startHour, "Wednesday", student) }
    </td>
    <td>
      { printCourse(startHour, "Thursday", student) }
    </td>
    <td>
      { printCourse(startHour, "Friday", student) }
    </td>
  }

  def printCourse(startHour: String, weekDay: String, student: Student) = {

    for (t <- Times.times) yield {
      val course: Course = Courses.courses.where(c => c.id === t.cID).single
      if (course.id == student.course1 || course.id == student.course2 || course.id == student.course3 || course.id == student.course4 || course.id == student.course5) {
        if (t.start == startHour && t.day == weekDay) {
          println(course.name)
          course.name
        }
      }
    }
  }

  def listMyCourses(): NodeSeq = {
    var stud: Student = null
    transaction {
      for (student <- Students.students) {
        if (student.name == studentName && student.surname == studentSurname) {
          stud = student
        }
      }
    }
    <table>
      <tr>
        {
          transaction {

            for (c <- Courses.courses) yield {
              if (isStudentEnrolled(stud, c) == true) {
                info(c, false, true)
              }

            }

          }
        }
      </tr>
    </table>

  }

  def listAllCourses(): NodeSeq = {
    var stud: Student = null
    transaction {
      for (student <- Students.students) {
        if (student.name == studentName && student.surname == studentSurname) {
          stud = student
        }
      }
    }
    <table>
      <tr>
        {
          transaction {

            for (c <- Courses.courses) yield {
              if (isStudentEnrolled(stud, c) == true) {
                info(c, false, true)
              } else {
                info(c, false, false)
              }
            }
          }
        }
      </tr>
    </table>
  }

  def numberOfLectures(course: Course): Int = {
    var counter = 0
    for (t <- Times.times) {
      if (course.id == t.cID) {
        counter += 1
      }
    }
    return counter
  }

  def setStudent() {
    val sessions = framework.sessionMap.keys
    for (session <- sessions) {
      if (session == framework.Count.getIdSession) {
        //if we find the key into the hashmap, we store the corresponding value, e.g, the student name
        studentName = framework.sessionMap(session).split(" ")(0)
        studentSurname = framework.sessionMap(session).split(" ")(1)
      } else {
        studentName = ""
        studentSurname = ""
      }
    }
  }

  def enroll(course: Long) {
    transaction {
      println("called", course)
      var stud: Student = null
      for (student <- Students.students) {
        if (student.name == studentName && student.surname == studentSurname) {
          stud = student
        }
      }
      var index = 0;
      var nCourses = stud.nOfcourses + 1

      if (stud.course1 == 0L) {
        index = 1
      } else if (stud.course2 == 0L) {
        index = 2
      } else if (stud.course3 == 0L) {
        index = 3
      } else if (stud.course4 == 0L) {
        index = 4
      } else if (stud.course5 == 0L) {
        index = 5
      }
      if (stud.nOfcourses < 5 && stud.course1 != course && stud.course2 != course && stud.course3 != course && stud.course4 != course) {
        if (index == 5) {
          update(Students.students)(s => where(s.name === studentName and s.surname === studentSurname)
            set (s.course5 := course, s.nOfcourses := nCourses))
        }
        if (index == 4) {
          update(Students.students)(s => where(s.name === studentName and s.surname === studentSurname)
            set (s.course4 := course, s.nOfcourses := nCourses))
        }
        if (index == 3) {
          update(Students.students)(s => where(s.name === studentName and s.surname === studentSurname)
            set (s.course3 := course, s.nOfcourses := nCourses))
        }
        if (index == 2) {
          update(Students.students)(s => where(s.name === studentName and s.surname === studentSurname)
            set (s.course2 := course, s.nOfcourses := nCourses))
        }
        if (index == 1) {
          update(Students.students)(s => where(s.name === studentName and s.surname === studentSurname)
            set (s.course1 := course, s.nOfcourses := nCourses))
        }
      }
    }

  }

  def unenroll(course: Long) {
    transaction {
      println("called", course)
      var stud: Student = null
      for (student <- Students.students) {
        if (student.name == studentName && student.surname == studentSurname) {
          stud = student
        }
      }

      var nCourses = stud.nOfcourses - 1
      if (stud.nOfcourses > 0) {
        update(Students.students)(s => where(s.name === studentName and s.surname === studentSurname and s.course1 === course)
          set (s.course1 := 0L, s.nOfcourses := nCourses))
        update(Students.students)(s => where(s.name === studentName and s.surname === studentSurname and s.course2 === course)
          set (s.course2 := 0L, s.nOfcourses := nCourses))
        update(Students.students)(s => where(s.name === studentName and s.surname === studentSurname and s.course3 === course)
          set (s.course3 := 0L, s.nOfcourses := nCourses))
        update(Students.students)(s => where(s.name === studentName and s.surname === studentSurname and s.course4 === course)
          set (s.course4 := 0L, s.nOfcourses := nCourses))
        update(Students.students)(s => where(s.name === studentName and s.surname === studentSurname and s.course5 === course)
          set (s.course5 := 0L, s.nOfcourses := nCourses))
      }
    }

  }

  def isStudentEnrolled(student: Student, course: Course) = {
    if (course.id == student.course1 || course.id == student.course2 || course.id == student.course3 || course.id == student.course4 || course.id == student.course5) {
      true
    }
  }

  def info(c: Course, recurse: Boolean, isEnrolled: Boolean): NodeSeq = {
    <tr>
      <tr>
        <td>
          { c.name }
        </td>
        <td colspan={ numberOfLectures(c).toString() }>
          { c.descritpion }
        </td>
        <td>
          Partecipants
        </td>
      </tr>
      <tr>
        <td>
          { c.prof }
        </td>
        {
          transaction {
            for (t <- Times.times) yield {
              if (c.id == t.cID) {
                timeInfo(t, false)
              }

            }
          }
        }
        <td>
          {
            if (isEnrolled == false) {
              <button class="enroll" id={ (c.id).toString() }>Enroll</button>
            } else {
              <button class="unenroll" id={ (c.id).toString() }>Unenroll</button>
            }
          }
        </td>
      </tr>
    </tr>
  }

  def timeInfo(time: Time, recurse: Boolean): NodeSeq = {
    <td>{ time.day } { time.start } - { time.end } </td>
    //      <td>{ time.start }</td>
    //      <td>{ time.end }</td>
  }
}